<template>
    <div class="universe-container">
        <div class="universe-content"></div>
        <div class="universe-code">
            <div class="universe-vertexShader">
                <textarea class="universe-glsl-code" v-model="vertexShader"></textarea>
                <span class="universe-title">顶点着色器的GLSL代码</span>
            </div>
            <div class="universe-fragmentShader">
                <textarea class="universe-glsl-code" v-model="fragmentShader"></textarea>
                <span class="universe-title">片段着色器的GLSL代码</span>
            </div>
        </div>
    </div>
</template>

<script>
import { AdditiveBlending, PointsMaterial, SceneManager, SceneParams, createBufferGeometry, createMaterial, createPoints, randomFromCylindricalCoordsVector3, randomVector3 } from '@/components/common/scenemanager';

//着色器参考
//https://threejs.org/docs/#api/zh/materials/ShaderMaterial
//https://webglfundamentals.org/webgl/lessons/zh_cn/webgl-how-it-works.html#toc

export default {
    name: "Universe",
    components: {},
    data() {
        return {
            vertexShader: `
uniform float size;
uniform float scale;
uniform float time;
varying vec3 vColor;
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <morphtarget_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
#ifdef USE_POINTS_UV
	varying vec2 vUv;
	uniform mat3 uvTransform;
#endif
void main() {
	#ifdef USE_POINTS_UV
	vUv = ( uvTransform * vec3( uv, 1 ) ).xy;
	#endif
	#include <color_vertex>
    //根据位置计算颜色用于片段着色器
    float d=length(abs(position)/vec3(40,10,40));
    vColor=mix(vec3(227,155,0),vec3(100,50,255),d)/255.0;
	#include <morphcolor_vertex>
	#include <begin_vertex>
    //位置随时间变化
    //float t=time;
    //float moveT=mod(t+position.x,PI2);
    //float moveS=mod(t+position.y,PI2);
    //transformed+=vec3(cos(moveS)*sin(moveT),cos(moveT),sin(moveS)*sin(moveT));
	#include <morphtarget_vertex>
	#include <project_vertex>
	gl_PointSize = size;
	#ifdef USE_SIZEATTENUATION
		bool isPerspective = isPerspectiveMatrix( projectionMatrix );
		if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );
	#endif
	#include <logdepthbuf_vertex>
	#include <clipping_planes_vertex>
	#include <worldpos_vertex>
	#include <fog_vertex>
}
            `,
            fragmentShader: `
uniform vec3 diffuse;
uniform float opacity;
varying vec3 vColor;
#include <common>
#include <color_pars_fragment>
#include <map_particle_pars_fragment>
#include <alphatest_pars_fragment>
#include <alphahash_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
void main() {
    //离中心的距离
    float d=length(gl_PointCoord.xy - 0.5);
    //距离大于0.5取0（透明）,小于0.1取1（不透明），中间透明度取d的值
    vec4 diffuseColor = vec4( vColor, smoothstep(0.5,0.1, d));
	#include <clipping_planes_fragment>
	vec3 outgoingLight = vec3( 0.0 );
	#include <logdepthbuf_fragment>
	#include <map_particle_fragment>
	#include <color_fragment>
	#include <alphatest_fragment>
	#include <alphahash_fragment>
	outgoingLight = diffuseColor.rgb;
	#include <opaque_fragment>
	#include <tonemapping_fragment>
	#include <colorspace_fragment>
	#include <fog_fragment>
	#include <premultiplied_alpha_fragment>
}
            `,
            time: 0,
        }
    },
    computed: {},
    methods: {
        //创建中心的球体
        createSphere() {
            return new Array(20000).fill().map(
                p => {
                    return randomVector3(Math.random() * 0.5 + 9.5);
                }
            );
        },
        //创建周围的星辰
        createStar() {
            let r = 10, R = 40;

            return new Array(20000).fill().map(
                p => {
                    let rand = Math.pow(Math.random(), 2);//让星辰靠近中心
                    let radius = Math.sqrt(R * R * rand + (1 - rand) * r * r);//星辰分布半径r-R
                    return randomFromCylindricalCoordsVector3(radius, Math.random() * 2 * Math.PI, (Math.random() - 0.5) * 2);
                }
            );
        },
        //创建点的材质
        createPointMaterial() {
            return createMaterial({
                size: 0.125,
                transparent: true,
                depthTest: false,
                blending: AdditiveBlending,
                //在材质编译之前修改颜色，替换顶点着色器和元片着色器，添加uniform和attribute以自定义颜色和移动
                onBeforeCompile: shader => {
                    shader.uniforms.time = { value: this.time };
                    shader.vertexShader = this.vertexShader;
                    shader.fragmentShader = this.fragmentShader;
                },
                customProgramCacheKey: () => {
                    return this.time + "";
                },
            }, PointsMaterial);
        },
        //旋转动画
        rotate() {
            const mesh = this.sceneManager.findObjectByName("universe");
            this.time = this.sceneManager.getElapsedTime() * 0.5 * Math.PI;
            const m=mesh.material;
            mesh.material=this.createPointMaterial();
            this.sceneManager.release(m);
            mesh.rotateY(0.0005);
        },
        //添加入场景
        addModel() {
            let sphere = this.createSphere();
            let star = this.createStar();
            let g = createBufferGeometry([...sphere, ...star]);
            let m = this.createPointMaterial();
            let mesh = createPoints("universe", g, m);
            mesh.rotateZ(0.2);//倾斜角度
            this.sceneManager.addModelToScene(0, mesh);

            this.draw();
        },
        //开始渲染
        draw() {
            const view = this.$el.querySelector(".universe-content");
            view.appendChild(this.sceneManager.getRenderDom());

            this.sceneManager.start();
        },
        resize() {
            const view = this.$el.querySelector(".universe-content");
            this.sceneManager.resize(view.offsetWidth, view.offsetHeight);
        },
        //场景参数
        createSceneParams() {
            const params = new SceneParams();
            params.cameraParams.fov = 60;
            params.cameraParams.near = 1;
            params.cameraParams.far = 1000;
            params.cameraParams.position.x = 0;
            params.cameraParams.position.y = 4;
            params.cameraParams.position.z = 21;

            params.drctLightParams.Intensity = 0.1;

            params.controlsParams.enablePan = false;
            params.controlsParams.enableZoom = false;
            params.controlsParams.enableRotate = true;
            params.controlsParams.enableDamping = true;
            return params;
        },
    },
    created() {

    },
    mounted() {
        const view = this.$el.parentElement.querySelector(".universe-content");
        this.sceneManager = new SceneManager(view.offsetWidth, view.offsetHeight, true);
        this.sceneManager.addScene(this.createSceneParams(), this.rotate);
        this.addModel();

        window.addEventListener('resize', this.resize);
    },
    beforeUnmount() {
        const view = this.$el.parentElement.querySelector(".universe-content");
        window.removeEventListener('resize', this.resize);
        this.sceneManager.destroy();
    },
}
</script>

<style scoped>
.universe-container {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
}

.universe-content {
    width: 100%;
    height: 100%;
}

.universe-code {
    position: fixed;
    top: -45%;
    width: 100%;
    height: 50%;
    display: flex;
    transition: top 0.5s;
}

.universe-code:hover {
    top: 0%;
}

.universe-vertexShader {
    position: absolute;
    left: 0%;
    width: 50%;
    height: 100%;
    background-color: rgb(154, 154, 217);
}

.universe-fragmentShader {
    position: absolute;
    left: 50%;
    width: 50%;
    height: 100%;
    background-color: rgb(184, 146, 227);
}

.universe-glsl-code {
    position: absolute;
    margin: 30px;
    width: 90%;
    height: 80%;
}

.universe-title {
    position: absolute;
    bottom: 0%;
    text-align: center;
    width: 100%;
    height: 30px;
    font-size: 20px;
    font-weight: bolder;
}
</style>